package com.icesoft.core.common.util;

import org.apache.commons.lang3.StringUtils;

import java.util.Hashtable;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexUtils {
    private static final Pattern ENGLISH_NUM_PATTERN = Pattern.compile("^[A-Za-z0-9]+");

    /**
     * 判断字符串是不是全部是英文字母+数字
     *
     * @param str
     * @return boolean
     */
    public static boolean isEngAndNum(String str) {
        return match(ENGLISH_NUM_PATTERN, str);
    }

    private static final Pattern PERCENT_PATTERN = Pattern.compile("(0\\.\\d+|\\d+(\\.\\d+)?%)");

    /**
     * 百分比，或大于0小于1的小数
     */
    public static boolean isPercent(String str) {
        return match(PERCENT_PATTERN, str);
    }

    private static final Pattern ENGLISH_NUM___PATTERN = Pattern.compile("^\\w+$");

    /**
     * 判断字符串是不是全部是英文字母+数字+下划线
     *
     * @param str
     * @return boolean
     */
    public static boolean isEngAndNum_(String str) {
        return match(ENGLISH_NUM___PATTERN, str);
    }

    private static final Pattern SPECIAL_CHAR_PATTERN = Pattern
            .compile("[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]");

    /**
     * 过滤特殊字符串 返回过滤后的字符串
     *
     * @param str
     * @return boolean
     */
    public static boolean isContainSpecialChar(String str) {
        return match(SPECIAL_CHAR_PATTERN, str);
    }

    private static final Pattern EMAIL_PATTERN = Pattern.compile("\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?");

    /**
     * 验证Email
     *
     * @param email email地址，格式：zhangsan@zuidaima.com，zhangsan@xxx.com.cn，xxx代表邮件服务商
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isEmail(String email) {
        return match(EMAIL_PATTERN, email);
    }

    private static final Pattern ENGLISH_PATTERN = Pattern.compile("^[A-Za-z]+$");

    /**
     * 判断字符串是不是全部是英文字母
     *
     * @param str
     * @return boolean
     */
    public static boolean isEnglish(String str) {
        return match(ENGLISH_PATTERN, str);
    }

    private static final Pattern MEBILE_PATTERN = Pattern.compile("1[3456789]\\d{9}$");

    /**
     * 验证11位手机号码（不支持国际格式，+86135xxxx...（中国内地），+00852137xxxx...（中国香港））
     *
     * @param mobile 移动、联通、电信运营商的号码段
     *               <p>
     *               移动的号段：134(0-8)、135、136、137、138、139、147（预计用于TD上网卡）
     *               、150、151、152、157（TD专用）、158、159、187（未启用）、188（TD专用）
     *               </p>
     *               <p>
     *               联通的号段：130、131、132、155、156（世界风专用）、185（未启用）、186（3g）
     *               </p>
     *               <p>
     *               电信的号段：133、153、180（未启用）、189
     *               </p>
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isMobile(String mobile) {
        return match(MEBILE_PATTERN, mobile);
    }

    private static final Pattern TELLPHONE_PATTERN = Pattern.compile("(\\d{3,4}\\-?)?\\d{7,8}$");

    /**
     * 验证固定电话号码，8位，或11位，或12位，或13位(带-)
     *
     * @param phone 电话号码，格式： 区号（城市代码） + 电话号码，如：02085588447
     *              <p>
     *              <b>国家（地区） 代码 ：</b>标识电话号码的国家（地区）的标准国家（地区）代码。它包含从 0 到 9
     *              的一位或多位数字， 数字之后是空格分隔的国家（地区）代码。
     *              </p>
     *              <p>
     *              <b>区号（城市代码）：</b>这可能包含一个或多个从 0 到 9 的数字，地区或城市代码放在圆括号——
     *              对不使用地区或城市代码的国家（地区），则省略该组件。
     *              </p>
     *              <p>
     *              <b>电话号码：</b>这包含从 0 到 9 的一个或多个数字
     *              </p>
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isTelephone(String phone) {
        return match(TELLPHONE_PATTERN, phone);
    }

    private static final Pattern DIGIT_PATTERN = Pattern.compile("\\-?\\d+");

    /**
     * 验证整数（正整数和负整数）
     *
     * @param digit 一位或多位0-9之间的整数
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isDigit(String digit) {
        return match(DIGIT_PATTERN, digit);
    }

    private static final Pattern DECIMAL_PATTERN = Pattern
            .compile("^(?!0+(?:\\.0+)?$)(?:[1-9]\\d*|0)(?:\\.\\d{1,2})?$");

    /**
     * 金额
     *
     * @param decimals 一位或多位0-9之间的浮点数，如：1.23，233.30
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isDecimals(String decimals) {
        return match(DECIMAL_PATTERN, decimals);
    }

    private static final Pattern CHINESE_PATTERN = Pattern.compile("^[\u4E00-\u9FA5]+$");

    /**
     * 验证中文
     *
     * @param chinese 中文字符
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isChinese(String chinese) {
        return match(CHINESE_PATTERN, chinese);
    }

    private static final Pattern URL_PATTERN = Pattern
            .compile("(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");

    /**
     * 验证URL地址
     *
     * @param url 格式：http://blog.csdn.net:80/xyang81/article/details/7705960? 或
     *            http://www.csdn.net:80
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isURL(String url) {
        return match(URL_PATTERN, url);
    }

    private static final Pattern POST_CODE_PATTERN = Pattern.compile("[1-9]\\d{5}");

    /**
     * 匹配中国邮政编码
     *
     * @param postcode 邮政编码
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isPostcode(String postcode) {
        return match(POST_CODE_PATTERN, postcode);
    }

    private static final Pattern IP_ADDRESS_PATTERN = Pattern
            .compile("^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");

    /**
     * 匹配IP地址(简单匹配，格式，如：192.168.1.1，127.0.0.1，没有匹配IP段的大小)
     *
     * @param ipAddress IPv4标准地址
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isIpAddress(String ipAddress) {
        // sun.net.util.IPAddressUtil
        if ("0:0:0:0:0:0:0:1".equals(ipAddress)) {
            return true;
        }
        return match(IP_ADDRESS_PATTERN, ipAddress);
    }

    // 年份
    static String strPattern = "^(19[4-9]\\d|20\\d{2})";

    static {
        strPattern += "(";
        // 月（1,3,5,7,8,10,12)
        strPattern += "((0[13578]|1[02])" + "(0[1-9]|[12][0-9]|3[01]))|";
        // 月(4,6,9,11)
        strPattern += "((0[469]|11)" + "(0[1-9]|[12][0-9]|30))|";
        // 月（2）
        strPattern += "(02" + "(0[1-9]|[12][0-9]))";
        strPattern += ")$";
    }

    private static final Pattern DATE_PATTERN = Pattern.compile(strPattern);

    /**
     * 判断字段是否为日期yyyy-MM-dd 符合返回ture
     *
     * @param str
     * @return boolean
     */
    public static boolean isDate(String str) {
        if (str.length() == 10) {
            String sep = "[-\\./_]";// 4种分隔符
            str = str.replaceAll(sep, "");
        }
        if (str.length() != 8) {
            return false;
        }

        return match(DATE_PATTERN, str);
    }

    private static final Pattern DATE_TIME_PATTERN = Pattern
            .compile("^[12]\\d{3}-(0[1-9]|1[0-2])-([012]\\d|3[01]) ([01]\\d|2[0-3])(:([0-5]\\d)){2}$");

    /**
     * 判断字段是否为日期时间yyyy-MM-dd HH:mm:ss 符合返回ture
     *
     * @param str
     * @return boolean
     */
    public static boolean isDateTime(String str) {
        return match(DATE_TIME_PATTERN, str);
    }

    private static Pattern PASSWORD_PATTERN = Pattern
            .compile("(?!.*[\\u4E00-\\u9FA5\\s])(?!^[a-zA-Z]+$)(?!^[\\d]+$)(?!^[^a-zA-Z\\d]+$)^.{8,32}$");

    /**
     * 密码要8-32位字母、数字、特殊字符最少2种组合（不能有中文和空格）
     */
    public static boolean isSafePassword(String password) {
        if (!PASSWORD_PATTERN.matcher(password).matches()) {
            return false;
        }
        return true;
    }

    /**
     * 匹配是否符合正则表达式pattern 匹配返回true
     *
     * @param pattern 匹配模式
     * @param str     匹配的字符串
     * @return boolean
     */
    private static boolean match(Pattern pattern, String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        }
        // Pattern p = Pattern.compile(pattern);
        Matcher m = pattern.matcher(str);
        return m.matches();
    }

    private static Pattern STRING_FORMAT_PATTERN = Pattern.compile("\\$\\{\\w+\\}");

    /**
     * 自定义渲染${name}模板，按name替换
     *
     * @param template 字符串模版
     * @param params   待替换参数
     * @return 渲染后的字符串
     */
    public static String processTemplate(String template, Map<String, String> params) {
        if (template == null || params == null) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        Matcher matcher = STRING_FORMAT_PATTERN.matcher(template);
        while (matcher.find()) {
            String param = matcher.group();
            String value = params.get(param.substring(2, param.length() - 1));
            matcher.appendReplacement(sb, value == null ? "" : value);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }



    /**
     * 验证身份证号码
     *
     * @param idCard 居民身份证号码18位，最后一位可能是数字或字母
     * @return 验证成功返回true，验证失败返回false
     */
    public static boolean isIdCard(String idCard) {
        if (idCard.length() != 18) {
            return false;
        }
        String area = idCard.substring(0, 2);
        if (hashtable.get(area) == null) {
            return false;
        }
        String date = idCard.substring(6, 14);
        if (!isDate(date)) {
            return false;
        }
        int[] prefix = new int[]{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        int[] suffix = new int[]{1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2};

        int idCardWiSum = 0; // 用来保存前17位各自乖以加权因子后的总和
        for (int i = 0; i < 17; i++) {
            idCardWiSum += Integer.valueOf(idCard.substring(i, i + 1)) * prefix[i];
        }

        int idCardMod = idCardWiSum % 11;// 计算出校验码所在数组的位置
        String idCardLast = idCard.substring(17);// 得到最后一位身份证号码

        // 如果等于2，则说明校验码是10，身份证号码最后一位应该是X
        if (idCardMod == 2) {
            if (idCardLast.equalsIgnoreCase("x")) {
                return true;
            } else {
                return false;
            }
        } else {
            // 用计算出的验证码与最后一位身份证号码匹配，如果一致，说明通过，否则是无效的身份证号码
            if (idCardLast.equals(suffix[idCardMod] + "")) {
                return true;
            } else {
                return false;
            }
        }
    }

    static Hashtable<String, String> hashtable = new Hashtable<>();

    static {
        hashtable.put("11", "北京");
        hashtable.put("12", "天津");
        hashtable.put("13", "河北");
        hashtable.put("14", "山西");
        hashtable.put("15", "内蒙古");

        hashtable.put("21", "辽宁");
        hashtable.put("22", "吉林");
        hashtable.put("23", "黑龙江");

        hashtable.put("31", "上海");
        hashtable.put("32", "江苏");
        hashtable.put("33", "浙江");
        hashtable.put("34", "安徽");
        hashtable.put("35", "福建");
        hashtable.put("36", "江西");
        hashtable.put("37", "山东");

        hashtable.put("41", "河南");
        hashtable.put("42", "湖北");
        hashtable.put("43", "湖南");
        hashtable.put("44", "广东");
        hashtable.put("45", "广西");
        hashtable.put("46", "海南");

        hashtable.put("50", "重庆");
        hashtable.put("51", "四川");
        hashtable.put("52", "贵州");
        hashtable.put("53", "云南");
        hashtable.put("54", "西藏");

        hashtable.put("61", "陕西");
        hashtable.put("62", "甘肃");
        hashtable.put("63", "青海");
        hashtable.put("64", "宁夏");
        hashtable.put("65", "新疆");

        hashtable.put("71", "台湾");
        hashtable.put("81", "香港");
        hashtable.put("82", "澳门");
        hashtable.put("91", "国外");
    }
}
